NFS3 protocol specific snapshot workflows#36
Conversation
|
I have tested Taking a CS-volume snapshot for the data CS-volume. |
…iecing is enabled and memory-disk is opted out
|
I have tested VM level and cloudstack-volume level snapshots, all found working
|
...ain/java/org/apache/cloudstack/storage/vmsnapshot/KvmFileBasedStorageVmSnapshotStrategy.java
Show resolved
Hide resolved
|
|
||
| @Override | ||
| public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { | ||
| s_logger.error("temp takeSnapshot : entered with snapshot id: " + snapshot.getId() + " and name: " + snapshot.getName()); |
There was a problem hiding this comment.
log level should not be error plus remove temp
There was a problem hiding this comment.
Yes, this was intentionally kept for testing and was mistakenly overlooked during cleanup.
| Map<String, String> poolDetails = storagePoolDetailsDao.listDetailsKeyPairs(volumeVO.getPoolId()); | ||
| StorageStrategy storageStrategy = Utility.getStrategyByStoragePoolDetails(poolDetails); | ||
|
|
||
| Map<String, String> cloudStackVolumeRequestMap = new HashMap<>(); |
There was a problem hiding this comment.
Get Volume Request is not as per protocol
There was a problem hiding this comment.
added protocol check.
| cloudStackVolumeRequestMap.put(Constants.VOLUME_UUID, poolDetails.get(Constants.VOLUME_UUID)); | ||
| cloudStackVolumeRequestMap.put(Constants.FILE_PATH, volumeVO.getPath()); | ||
| CloudStackVolume cloudStackVolume = storageStrategy.getCloudStackVolume(cloudStackVolumeRequestMap); | ||
| if (cloudStackVolume == null || cloudStackVolume.getFile() == null) { |
There was a problem hiding this comment.
this condition is also specific to file, it will fail for lun
| throw new CloudRuntimeException("takeSnapshot: Failed to get source file to take snapshot"); | ||
| } | ||
| long capacityBytes = storagePool.getCapacityBytes(); | ||
| s_logger.error("temp takeSnapshot : entered after getting cloudstack volume with file path: " + cloudStackVolume.getFile().getPath() + " and size: " + cloudStackVolume.getFile().getSize()); |
| long capacityBytes = storagePool.getCapacityBytes(); | ||
| s_logger.error("temp takeSnapshot : entered after getting cloudstack volume with file path: " + cloudStackVolume.getFile().getPath() + " and size: " + cloudStackVolume.getFile().getSize()); | ||
| long usedBytes = getUsedBytes(storagePool); | ||
| long fileSize = cloudStackVolume.getFile().getSize(); |
There was a problem hiding this comment.
these all changes are related to file
|
|
||
| String fileSnapshotName = volumeInfo.getName() + "-" + snapshot.getUuid(); | ||
|
|
||
| int maxSnapshotNameLength = 64; |
There was a problem hiding this comment.
I think, we can have more character in snapshot name
There was a problem hiding this comment.
moved it to contants for now, will update the length by checking it, if required.
| return cloudStackVolumeRequest; | ||
| } | ||
|
|
||
| private CloudStackVolume snapshotCloudStackVolumeRequestByProtocol(Map<String, String> details, |
There was a problem hiding this comment.
I am calling this method in takeSnapshot()
...volume/ontap/src/main/java/org/apache/cloudstack/storage/service/model/CloudStackVolume.java
Show resolved
Hide resolved
rajiv-jain-netapp
left a comment
There was a problem hiding this comment.
reposting the PR with changes
...ain/java/org/apache/cloudstack/storage/vmsnapshot/KvmFileBasedStorageVmSnapshotStrategy.java
Show resolved
Hide resolved
|
|
||
| @Override | ||
| public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { | ||
| s_logger.error("temp takeSnapshot : entered with snapshot id: " + snapshot.getId() + " and name: " + snapshot.getName()); |
There was a problem hiding this comment.
Yes, this was intentionally kept for testing and was mistakenly overlooked during cleanup.
| Map<String, String> poolDetails = storagePoolDetailsDao.listDetailsKeyPairs(volumeVO.getPoolId()); | ||
| StorageStrategy storageStrategy = Utility.getStrategyByStoragePoolDetails(poolDetails); | ||
|
|
||
| Map<String, String> cloudStackVolumeRequestMap = new HashMap<>(); |
There was a problem hiding this comment.
added protocol check.
| cloudStackVolumeRequestMap.put(Constants.VOLUME_UUID, poolDetails.get(Constants.VOLUME_UUID)); | ||
| cloudStackVolumeRequestMap.put(Constants.FILE_PATH, volumeVO.getPath()); | ||
| CloudStackVolume cloudStackVolume = storageStrategy.getCloudStackVolume(cloudStackVolumeRequestMap); | ||
| if (cloudStackVolume == null || cloudStackVolume.getFile() == null) { |
| throw new CloudRuntimeException("takeSnapshot: Failed to get source file to take snapshot"); | ||
| } | ||
| long capacityBytes = storagePool.getCapacityBytes(); | ||
| s_logger.error("temp takeSnapshot : entered after getting cloudstack volume with file path: " + cloudStackVolume.getFile().getPath() + " and size: " + cloudStackVolume.getFile().getSize()); |
| long capacityBytes = storagePool.getCapacityBytes(); | ||
| s_logger.error("temp takeSnapshot : entered after getting cloudstack volume with file path: " + cloudStackVolume.getFile().getPath() + " and size: " + cloudStackVolume.getFile().getSize()); | ||
| long usedBytes = getUsedBytes(storagePool); | ||
| long fileSize = cloudStackVolume.getFile().getSize(); |
|
|
||
| String fileSnapshotName = volumeInfo.getName() + "-" + snapshot.getUuid(); | ||
|
|
||
| int maxSnapshotNameLength = 64; |
There was a problem hiding this comment.
moved it to contants for now, will update the length by checking it, if required.
| return cloudStackVolumeRequest; | ||
| } | ||
|
|
||
| private CloudStackVolume snapshotCloudStackVolumeRequestByProtocol(Map<String, String> details, |
There was a problem hiding this comment.
I am calling this method in takeSnapshot()
...volume/ontap/src/main/java/org/apache/cloudstack/storage/service/model/CloudStackVolume.java
Show resolved
Hide resolved
| mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.FALSE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.FALSE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString()); |
There was a problem hiding this comment.
Why do we need to set this as true as we are currently not supporting volume creation from snapshot.
There was a problem hiding this comment.
Without this property set to true, CloudStack initiates both the snapshot creation and the corresponding copy operation to secondary storage over the data path, as it assumes that snapshots must reside on secondary storage by default.
When only the storage-system-snapshot property is enabled, CloudStack successfully invokes the takeSnapshot() method in the plugin driver class. However, the snapshot remains on the primary storage pool and is not transferred to secondary storage.
There was a problem hiding this comment.
So, with this implementation, snapshots reside in primary storage itself?
There was a problem hiding this comment.
What I’m trying to explain is that if we don’t keep the second flag set to true, then only the plugin‑created snapshot will remain on the primary. In that case, the orchestrator will call the host agent again to create another snapshot and copy it to the secondary.
To avoid this unnecessary duplicate operation, I set both flags to true. This ensures that the orchestrator does not rely on the host-agent portion of the workflow, and the snapshot created by the plugin is the one that gets copied to the secondary.
...me/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java
Outdated
Show resolved
Hide resolved
| return new Pair<>(snapshotXml, volumeObjectToNewPathMap); | ||
| } | ||
|
|
||
| protected void cleanupLeftoverDeltas(List<VolumeObjectTO> volumeObjectTos, Map<String, Pair<Long, String>> mapVolumeToSnapshotSizeAndNewVolumePath, KVMStoragePoolManager storagePoolMgr) { |
There was a problem hiding this comment.
Does this refactoring somehow help our plugin functionality?
| mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.FALSE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.FALSE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString()); |
There was a problem hiding this comment.
We are supporting volume creation from snapshot with this PR?
There was a problem hiding this comment.
volume creation usecase is not yet ready as part of this PR
|
|
||
| @Override | ||
| public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { | ||
| s_logger.info("takeSnapshot : entered with snapshot id: " + snapshot.getId() + " and name: " + snapshot.getName()); |
There was a problem hiding this comment.
Lets have an intuitive log message instead of a method entry logger?
There was a problem hiding this comment.
Could you provide a few examples?
Since I am also logging the method arguments, I felt that this approach would give me better insight into the method invocation flow, rather than relying solely on static logging.
There was a problem hiding this comment.
Instead of adding an entry log we can add a log post line 544. For example, it could be like
s_logger.info("takeSnapshot: Snapshot with ID: " + snapshot.getId() + " and name: " + snapshot.getName() + " requested for volume with ID: " + volumeVO.getId() + " and name: " + volumeVO.getName())
It's just an example but I feel this could give us more outlook on whats happening.
...me/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java
Outdated
Show resolved
Hide resolved
| cloudStackVolumeRequestMap.put(Constants.FILE_PATH, volumeVO.getPath()); | ||
| cloudStackVolume = storageStrategy.getCloudStackVolume(cloudStackVolumeRequestMap); | ||
| if (cloudStackVolume == null || cloudStackVolume.getFile() == null) { | ||
| throw new CloudRuntimeException("takeSnapshot: Failed to get source file to take snapshot"); |
There was a problem hiding this comment.
Should we have method names only in loggers and not have them in exceptions? Management server might show this as an error on the UI?
| try { | ||
| fileResponse = nasFeignClient.getFileResponse(authHeader, volumeUuid, filePath); | ||
| if (fileResponse == null || fileResponse.getRecords().isEmpty()) { | ||
| throw new CloudRuntimeException("File " + filePath + " not not found on ONTAP. " + |
There was a problem hiding this comment.
Still see the same
...me/ontap/src/main/java/org/apache/cloudstack/storage/vmsnapshot/OntapVMSnapshotStrategy.java
Show resolved
Hide resolved
...me/ontap/src/main/java/org/apache/cloudstack/storage/vmsnapshot/OntapVMSnapshotStrategy.java
Show resolved
Hide resolved
| return false; | ||
| } | ||
|
|
||
| if (!Hypervisor.HypervisorType.KVM.equals(userVm.getHypervisorType())) { |
There was a problem hiding this comment.
We already have checks in StoragePool and CS Volume creation workflows for Hypervisor type right? Do we need it here also?
There was a problem hiding this comment.
This is inline with the snapshot workflow
| return false; | ||
| } | ||
|
|
||
| if (!VirtualMachine.State.Running.equals(userVm.getState())) { |
There was a problem hiding this comment.
Wouldn't this block disk snapshots?
...me/ontap/src/main/java/org/apache/cloudstack/storage/vmsnapshot/OntapVMSnapshotStrategy.java
Show resolved
Hide resolved
...me/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java
Show resolved
Hide resolved
|
|
||
| updateSnapshotDetails(snapshot.getId(), volumeInfo.getId(), poolDetails.get(Constants.VOLUME_UUID), cloneCloudStackVolume.getFile().getPath(), volumeVO.getPoolId(), fileSize); | ||
|
|
||
| snapshotObjectTo.setPath(Constants.ONTAP_SNAP_ID +"="+cloneCloudStackVolume.getFile().getPath()); |
| CloudStackVolume snapCloudStackVolumeRequest = snapshotCloudStackVolumeRequestByProtocol(poolDetails, volumeVO.getPath(), snapshotName); | ||
| CloudStackVolume cloneCloudStackVolume = storageStrategy.snapshotCloudStackVolume(snapCloudStackVolumeRequest); | ||
|
|
||
| updateSnapshotDetails(snapshot.getId(), volumeInfo.getId(), poolDetails.get(Constants.VOLUME_UUID), cloneCloudStackVolume.getFile().getPath(), volumeVO.getPoolId(), fileSize); |
There was a problem hiding this comment.
If iSCSI needs different things to be updated in DB, we might get an if-else on protocol here also.
There was a problem hiding this comment.
yes, we have to handle that with iscsi protocol.
| if (volume.getPoolId() == null) { | ||
| return false; | ||
| } | ||
| StoragePoolVO pool = storagePool.findById(volume.getPoolId()); |
There was a problem hiding this comment.
We can optimize here -> Instead of making findById for each volume, we can use listByUuids by passing all volumes together. This will save much db calls and memory.
There was a problem hiding this comment.
DB calls are local and not very expensive.
| boolean quiescevm = true; | ||
| VMSnapshotOptions options = vmSnapshotVO.getOptions(); | ||
| if (options != null && !options.needQuiesceVM()) { | ||
| logger.info("Quiesce option was set to false, but overriding to true for ONTAP managed storage " + |
There was a problem hiding this comment.
If we are planning to only support app consistency, why not throw an error message stating ONTAP recommends quiescing the vm instead of overriding in backend without notifying user.
There was a problem hiding this comment.
Logic has been changed to adhering the user input on quiecing
…ge level support.
…ustomized class only.
…shot with driver orchestrator workflow
…stopped state both.
| // TODO Set it to false once we start supporting snapshot feature | ||
| mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.FALSE.toString()); | ||
| mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.FALSE.toString()); | ||
| // STORAGE_SYSTEM_SNAPSHOT=TRUE enables StorageSystemSnapshotStrategy to handle snapshots. |
There was a problem hiding this comment.
Please remove comments if applicable
| } | ||
| } | ||
|
|
||
| /** |
There was a problem hiding this comment.
these commented code was left in earlier merges, I will remove it.
| <bean id="ontapPrimaryDataStoreProvider" | ||
| class="org.apache.cloudstack.storage.provider.OntapPrimaryDatastoreProvider" /> | ||
|
|
||
| <bean id="ontapVMSnapshotStrategy" |
There was a problem hiding this comment.
Why not inject it through configure method of OntapPrimaryDatastoreProvider which will also follow cloudstack injection framework.
There was a problem hiding this comment.
this change is for context to be given tot he cloudstack for the classes we added and we want them to be loaded in the JVM
There was a problem hiding this comment.
I think that way also , it will give context to the cloudstack and will be loaded in JVM once ontap driver is loaded.
| this.sanFeignClient = feignClientFactory.createClient(SANFeignClient.class, baseURL); | ||
| } | ||
|
|
||
| @Override |
There was a problem hiding this comment.
why do we need this here again? We already have sanFeignClient in it constructor.
There was a problem hiding this comment.
right, corrected it now
| SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO) snapshot.getTO(); | ||
|
|
||
| // Build snapshot name using volume name and snapshot UUID | ||
| String snapshotName = buildSnapshotCloneName(volumeInfo.getName(), snapshot.getUuid()); |
There was a problem hiding this comment.
It is just a method name, but corrected.
There was a problem hiding this comment.
If you notice, it is just a method name. But I will update this.
|
|
||
| VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId()); | ||
| if (volumeVO == null) { | ||
| throw new CloudRuntimeException("takeSnapshot: VolumeVO not found for id: " + volumeInfo.getId()); |
There was a problem hiding this comment.
Please don't add method names in exceptions, as we don't have any cleanup framework before showing them on UI
There was a problem hiding this comment.
I understand. I was watching such instances, some of which are still overlooked.
| */ | ||
| @Override | ||
| public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { | ||
| public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, |
There was a problem hiding this comment.
I see that 'CAN_CREATE_VOLUME_FROM_SNAPSHOT' in the capabilities list has been set to 'True', but createAsync doesn't have any implementation to create volume from snapshot. So, if we are not supporting creating volume from snapshot, do we need revertSnapshot implementation?
There was a problem hiding this comment.
We’ve made good progress on the revert while working in parallel on the snapshot design, since both need to align. Our first PR is still awaiting community review, which is giving us additional time to advance the revert work. I plan to finalize the revert workflow once we complete the upcoming planned milestone. If time permits, the goal is to include the revert changes in the next PR as well.
piyush5netapp
left a comment
There was a problem hiding this comment.
Please add testing screenshot .
Description
This PR...
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
How did you try to break this feature and the system with this change?